#ifndef GLOBAL_DEFINES_HPP
#define GLOBAL_DEFINES_HPP

#include "glew.h"
#include "Window.hpp"
//#include "ftgl.h"
//#include "math.h"
#include <iostream>
#include <sstream>
#include <string>
#include <cassert>
#include <SupportStructs.hpp>

// protect, since it's a common define
#ifndef PI
#define PI 3.141592653589
#endif

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

// global error margin
#ifndef EPSILON
#define EPSILON 0.0001f
#endif

#define GLOBAL_NEAR_PLANE 0.1

#define GLOBAL_FAR_PLANE 500.0

#define GLOBAL_FOV 60

#define TIMERSECS 18

// mouse btn down or up
namespace sf
{
    enum MouseState
    {
        Up,
        Down
    };
}

inline double rad(double degree)
{
  return degree*PI/180.0f;
}

static std::string fToS (double number)
{
  std::ostringstream num;
  num << number;
  return num.str();
}

static double sToF (std::string string)
{
  std::istringstream iss(string);
  double res;
  if( (iss >> std::dec >> res).fail() )
  {
      return INT_MAX;
  }
  return res;
  return -1.0;
}

static double POW(double num, int power)
{
  if (power == 0)
      return 1;
  bool lessThanZero = power < 0; 
  int pw = lessThanZero ? -power : power;
  double res = 1;
  for (int i = 1; i <= pw; i++)
  {
      res *= num;
  }
  if (lessThanZero)
      return 1/res;
  return res;
}

static void GLCheckError(const std::string& file, unsigned int line)
{
  // Get the last error
  GLenum errorCode = glGetError();

  if (errorCode != GL_NO_ERROR)
  {
      std::string error = "unknown error";
      std::string description  = "no description";

      // Decode the error code
      switch (errorCode)
      {
          case GL_INVALID_ENUM :
          {
              error = "GL_INVALID_ENUM";
              description = "an unacceptable value has been specified for an enumerated argument";
              break;
          }

          case GL_INVALID_VALUE :
          {
              error = "GL_INVALID_VALUE";
              description = "a numeric argument is out of range";
              break;
          }

          case GL_INVALID_OPERATION :
          {
              error = "GL_INVALID_OPERATION";
              description = "the specified operation is not allowed in the current state";
              break;
          }

          case GL_STACK_OVERFLOW :
          {
              error = "GL_STACK_OVERFLOW";
              description = "this command would cause a stack overflow";
              break;
          }

          case GL_STACK_UNDERFLOW :
          {
              error = "GL_STACK_UNDERFLOW";
              description = "this command would cause a stack underflow";
              break;
          }

          case GL_OUT_OF_MEMORY :
          {
              error = "GL_OUT_OF_MEMORY";
              description = "there is not enough memory left to execute the command";
              break;
          }

          case GL_INVALID_FRAMEBUFFER_OPERATION_EXT :
          {
              error = "GL_INVALID_FRAMEBUFFER_OPERATION_EXT";
              description = "the object bound to FRAMEBUFFER_BINDING_EXT is not \"framebuffer complete\"";
              break;
          }
      }

      // Log the error
	std::cout << "An internal OpenGL call failed in "
            << file.substr(file.find_last_of("\\/") + 1) << " (" << line << ") : "
            << error << ", " << description
            << std::endl;
  }
}

#define GLCheck(call) ((call), GLCheckError(__FILE__, __LINE__))


#endif
